﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using SMSROUTER.Business;
using System.Threading.Tasks;
using Settings;
using StorageManagement;
using System.Configuration;
using System.Threading;
using System.Web.Services.Protocols;
using SMSROUTER.StorageManagement;

namespace PluggableModulesRouter
{
    public abstract class WorkingBaseMORouter : WorkingBaseRouter
    {
        #region SyncLoop
        public override void Start()
        {
            Stop();
            if (this.Router != null)
            {
                _IsStart = true;
                if (taskList.Count < NumOfThread)
                {
                    for (int i = 0; i < NumOfThread; i++)
                    {
                        taskList.Add(Task.Factory.StartNew(ActionSent, TaskCreationOptions.LongRunning));
                    }
                }

                this.Router.Started = _IsStart;
                this.Router.Save();
            }
        }

        protected void ActionSent()
        {
            try
            {
                while (_IsStart)
                {
                    try
                    {
                        Processing();
                    }
                    catch (AggregateException ex)
                    {
                        foreach (var item in ex.InnerExceptions) HandleException(ex, AppSetting.WarningType.mo_error);
                    }
                    catch (Exception ex)
                    {
                        HandleException(ex, AppSetting.WarningType.mo_error);
                    }
                    finally
                    {
                        if (_IsStart)
                        {
                            Thread.Sleep(INFINITE_NEXT_TIME);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, AppSetting.WarningType.mo_error);
            }
        }

        private void Processing()
        {
            var listSmsInWaiting = SmsInWaitingStorage.DequeueElementData(Key, AppSetting.BatchSize);
            if (listSmsInWaiting != null && listSmsInWaiting.Count > 0)
            {                
                PreSendSms(listSmsInWaiting);
            }
        }

        private void PreSendSms(IList<SmsInWaiting> smsList)
        {
            Dictionary<int, List<SmsInWaiting>> dic = new Dictionary<int, List<SmsInWaiting>>();
            foreach (var item in smsList)
            {
                if (item.CooperateID != null)
                {
                    if (!dic.ContainsKey(item.CooperateID.Value)) dic.Add(item.CooperateID.Value, new List<SmsInWaiting>());
                    dic[item.CooperateID.Value].Add(item);
                }
                else
                {
                    SmsInStatisticStorage.EnqueueElementData(Key, new SmsInStatisticStruct(AppSetting.Status.FAIL, item.SmsInGUID, item.RetryOnFailReqTimes, item.RetryOnTimeOutReqTimes, item.RetryOnFailReqDuration, item.RetryOnTimeOutReqDuration, ThreadID, item.RouterID, item.LastTimeProcess));
                    Log.InsertLog(string.Format("[SmsInGUID = {0}] erorr: {1}", item.SmsInGUID, "CooperateID is null"), "WorkingBaseMORouter.PreSendSms", ObjectType, AppSetting.WarningType.mo_error, item.CooperateID);
                }
            }

            if (dic.Count > 0)
            {
                Parallel.ForEach(dic, item =>
                {
                    try
                    {
                        CooperateProvider cooperate = CooperateProvider.GetByID(item.Key);
                        SmsWebService smsWebService = SmsWebService.GetByCooperateIDAndRouterID(item.Key, this.Key);
                        if (cooperate != null && smsWebService != null)
                        {
                            SendSms(item.Value, cooperate, smsWebService);
                        }
                        else
                        {
                            string s = "";
                            if (cooperate == null) s += "Cooperate";
                            if (smsWebService == null)
                            {
                                if (s != "") s += " and ";
                                s += "SmsWebService";
                            }
                            s += " is null.";
                            RetryOnFailure<SmsInWaiting>(item.Value, s);
                        }
                    }
                    catch (Exception ex)
                    {
                        HandleException(ex, AppSetting.WarningType.mo_error, item.Value);
                    }
                });
            }
        }

        protected abstract void SendSms(List<SmsInWaiting> smsInWaitingList, CooperateProvider cooperate, SmsWebService smsWebService);
        #endregion SyncLoop

        #region Handle Exception
        /// <summary>
        /// Xử lý tin nhắn khi fail
        /// </summary>
        protected override void RetryOnFailure(dynamic obj, string errorMessage)
        {
            var sms = obj as SmsInWaiting;
            if (sms != null)
            {
                if ((sms.RetryOnFailReqTimes??0) < this.RetryOnFailReqTimes)
                {
                    sms.RetryOnFailReqTimes = (sms.RetryOnFailReqTimes ?? 0) + 1;
                    SmsInWaitingStorage.EnqueuData(sms);
                    Log.InsertLog(string.Format("Retry On Failure [SmsInGUID = {0}]. Retry times: {1}. Error: {2}", sms.SmsInGUID, sms.RetryOnFailReqTimes, errorMessage), "RetryOnFailure", ObjectType, AppSetting.WarningType.mo_error, sms.CooperateID);
                }
                else
                {
                    SmsInStatisticStorage.SetStatus(Key, sms, DateTime.Now, AppSetting.Status.FAIL, ThreadID);
                    Log.InsertLog(string.Format("Retry On Failure [SmsInGUID = {0}], Set Status = '{1}'. Error: {2}", sms.SmsInGUID, AppSetting.Status.FAIL, errorMessage), "RetryOnFailure - FAIL", ObjectType, AppSetting.WarningType.mo_error, sms.CooperateID);
                }
            }
        }

        /// <summary>
        /// Xử lý tin nhắn khi timed out
        /// </summary>
        protected override void RetryOnTimedOut(dynamic obj, string errorMessage)
        {
            var sms = obj as SmsInWaiting;
            if (sms != null)
            {
                if ((sms.RetryOnTimeOutReqTimes ?? 0) < this.RetryOnTimeoutReqTimes)
                {
                    sms.RetryOnTimeOutReqTimes = (sms.RetryOnTimeOutReqTimes ?? 0) + 1;
                    SmsInWaitingStorage.EnqueuData(sms);
                    Log.InsertLog(string.Format("Retry On Timeout [SmsInGUID = {0}]. Retry times: {1}. Error: {2}", sms.SmsInGUID, sms.RetryOnTimeOutReqTimes, errorMessage), "RetryOnTimedOut", ObjectType, AppSetting.WarningType.mo_error, sms.CooperateID);
                }
                else
                {
                    SmsInStatisticStorage.SetStatus(Key, sms, DateTime.Now, AppSetting.Status.TIMEOUT, ThreadID);
                    Log.InsertLog(string.Format("Retry On Timeout [SmsInGUID = {0}], Set Status = '{1}'. Error: {2}", sms.SmsInGUID, AppSetting.Status.FAIL, errorMessage), "RetryOnTimedOut - FAIL", ObjectType, AppSetting.WarningType.mo_error, sms.CooperateID);
                }
            }
        }
        #endregion Handle Exception
    }
}